// Copyright (c) 2025, 大连理工大学 (Dalian University of Technology)
//
// Licensed under the Mulan PSL v2.
// You can use this file according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of the License at
//
//     http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. SEE THE MULAN PSL v2
// FOR MORE DETAILS.

/*
 * @Author: qiulin_JXGM ql1746365080@gmail.com
 * @Date: 2024-10-16 09:35:57
 * @LastEditors: qiulin 1746365080@qq.com
 * @LastEditTime: 2025-01-02 17:22:43
 * @FilePath: \SGFEM\Import\BDFImport\BulkData\Property\PCOMPParser.cpp
 */
#include "PCOMPParser.h"

#include <cstdint>

#include "boost/algorithm/string.hpp"

#include "Utility/Logging/SGLogger.h"

#include "DBManager/DBServiceFactory.h"
#include "DBManager/IService/Input/Property/IPCOMPService.h"

#include "Import/BDFImport/BDFParserBase.h"

BULKDATA_ENTRY_PARSER_REG (PCOMP);

using namespace SG::DBManager;
using namespace SG::Import;
using namespace SG::Import::BDF;
using namespace SG::DataStructure;
/*
// file://./../../Doc/MarkDown/BulkData/PCOMP/PCOMP.md>
*/
enum PCOMPIndex : std::uint8_t
{
    PCOMPIndex_PID  = 1,
    PCOMPIndex_Z0   = 2,
    PCOMPIndex_NSM  = 3,
    PCOMPIndex_SB   = 4,
    PCOMPIndex_FT   = 5,
    PCOMPIndex_TREF = 6,
    PCOMPIndex_GE   = 7,
    PCOMPIndex_LAM  = 8,
};

enum PCOMPLayIndex : std::uint8_t
{
    PCOMPLayIndex_MID   = 0,
    PCOMPLayIndex_T     = 1,
    PCOMPLayIndex_THETA = 2,
    PCOMPLayIndex_SOUT  = 3,
};
namespace
{
    /**
     * @brief 解析一般的PCOMP数据，并将其存储到PCOMPData结构中。
     *
     * 该函数处理PCOMP卡片中的一般数据，如材料属性、层叠顺序、应力应变方法等。
     * 根据PCOMP的索引位置，将字符串形式的分割数据转换为具体的数值或枚举，并存储在`tempData`中。
     *
     * @param dividedEntryInfo 包含分割的输入信息的字符串向量。
     * @param tempData 用于存储解析后数据的PCOMPData数据结构。
     */
    void parseGeneralData (std::vector<std::string>& dividedEntryInfo, SG::DataStructure::FEM::PCOMPData& tempData)
    {
        std::string curStrKey;  // 当前处理的字符串
        // 遍历索引1到8的字段
        for (int curIndex = 1; curIndex < 9; curIndex++)
        {
            curStrKey = dividedEntryInfo[curIndex];  // 获取当前字段的字符串

            // 判断该字段是否为空
            bool const isEmpty = is_all_space (curStrKey);
            if (isEmpty)
            {
                continue;  // 如果该字段为空，跳过处理
            }

            // 将当前字符串转换为科学计数法

            // 根据当前索引的位置处理相应的字段数据
            switch (curIndex)
            {
            case PCOMPIndex_PID:
                // 将PID字段转换为长整型并存储
                tempData.m_id = convertToI64 (curStrKey);
                break;
            case PCOMPIndex_Z0:
                // 将Z0字段转换为双精度浮点数并存储
                tempData.m_offset = convertToReal (curStrKey);
                break;
            case PCOMPIndex_NSM:
                // 将NSM字段转换为双精度浮点数并存储
                tempData.m_nsm = convertToReal (curStrKey);
                break;
            case PCOMPIndex_SB:
                // 将SB字段转换为双精度浮点数并存储
                tempData.m_sb = convertToReal (curStrKey);
                break;
            case PCOMPIndex_FT:
                // 处理FT字段，依据字符串内容设置对应的应力/应变方法枚举值
                boost::trim (curStrKey);  // 去除字符串两端的空格
                if ("HILL" == curStrKey)
                {
                    tempData.m_ft = 1;
                }
                else if ("HOFF" == curStrKey)
                {
                    tempData.m_ft = 2;
                }
                else if ("TSAI" == curStrKey)
                {
                    tempData.m_ft = 3;
                }
                else if ("STRN" == curStrKey)
                {
                    tempData.m_ft = 4;
                }
                else if ("HFAIL" == curStrKey)
                {
                    tempData.m_ft = 5;
                }
                else if ("HTAPE" == curStrKey)
                {
                    tempData.m_ft = 6;
                }
                else if ("HFABR" == curStrKey)
                {
                    tempData.m_ft = 7;
                }
                break;
            case PCOMPIndex_TREF:
                // 将TREF字段转换为双精度浮点数并存储
                tempData.m_tref = convertToReal (curStrKey);
                break;
            case PCOMPIndex_GE:
                // 将GE字段转换为双精度浮点数并存储
                tempData.m_ge = convertToReal (curStrKey);
                break;
            case PCOMPIndex_LAM:
                // 处理LAM字段，根据字符串内容设置层压选项
                boost::trim (curStrKey);  // 去除字符串两端的空格
                if ("SYM" == curStrKey)
                {
                    tempData.m_lam = SG::DataStructure::FEM::LaminateOption_SYM;
                }
                else if ("MEM" == curStrKey)
                {
                    tempData.m_lam = SG::DataStructure::FEM::LaminateOption_MEM;
                }
                else if ("BEND" == curStrKey)
                {
                    tempData.m_lam = SG::DataStructure::FEM::LaminateOption_BEND;
                }
                else if ("SMEAR" == curStrKey)
                {
                    tempData.m_lam = SG::DataStructure::FEM::LaminateOption_SMEAR;
                }
                else if ("SMCORE" == curStrKey)
                {
                    tempData.m_lam = SG::DataStructure::FEM::LaminateOption_SMCORE;
                }
                break;
            default:
                break;
            }
        }
    }

    /**
     * @brief 解析单层的PCOMP层数据，并存储到PCOMPData数据结构中。
     *
     * 该函数解析一层数据，可能包含材料ID、厚度、角度以及是否有剪切输出等信息。每一行可能包含多个ply（层），
     * 如果存在有效的ply数据，则将该ply信息存储到`tempData`的ShellSectionLayer中。
     *
     * @param dividedEntryInfo 包含分割的输入信息的字符串向量。
     * @param baseIndex 当前行层数据的基础索引，用于定位相关的数据字段。
     * @param tempData 用于存储解析后层数据的PCOMPData数据结构。
     */
    void parseSingleLayData (std::vector<std::string>& dividedEntryInfo, size_t baseIndex, SG::DataStructure::FEM::PCOMPData& tempData)
    {
        // 标记当前行是否有有效的ply（层）。初始为false。
        bool isHaveply = false;

        // 用于记录每个字段是否为空的布尔数组，以及相应字段的索引数组。
        bool   recordEmpty[4] = { false };
        size_t recordIndex[4] = { 0 };

        // 遍历当前行的四个字段，分别检查每个字段是否为空。
        for (size_t i = 0; i < 4; i++)
        {
            // 计算当前字段的索引。
            recordIndex[i] = baseIndex + i;
            // 检查该字段是否全部为空格。
            recordEmpty[i] = std::all_of (
                dividedEntryInfo[recordIndex[i]].begin (), dividedEntryInfo[recordIndex[i]].end (), [] (char c) { return c == ' '; });

            // 如果字段不为空，说明该行存在有效的ply数据。
            if (!recordEmpty[i])
            {
                isHaveply = true;  // 见备注2
            }
        }

        // 如果存在有效的ply数据，解析并存储到tempData中。
        if (isHaveply)
        {
            SG::DataStructure::FEM::ShellSectionLayer layer1;

            // 遍历每个字段，解析对应的材料ID、厚度、角度和剪切输出信息。
            for (size_t i = 0; i < 4; i++)
            {
                switch (i)
                {
                // 对于材料ID (MID) 和 厚度 (T)，如果字段为空，则默认使用上一层的数值，见备注1。
                case PCOMPLayIndex_MID:
                    if (recordEmpty[i])
                    {
                        // 使用上一层的材料ID
                        layer1.m_matId = tempData.m_secData.back ().m_matId;
                    }
                    else
                    {
                        // 解析当前层的材料ID
                        layer1.m_matId = convertToI64 (dividedEntryInfo[recordIndex[i]]);
                    }
                    break;

                case PCOMPLayIndex_T:
                    if (recordEmpty[i])
                    {
                        // 使用上一层的厚度
                        layer1.m_thickness = tempData.m_secData.back ().m_thickness;
                    }
                    else
                    {
                        // 解析当前层的厚度
                        layer1.m_thickness = convertToReal (dividedEntryInfo[recordIndex[i]]);
                    }
                    break;

                case PCOMPLayIndex_THETA:
                    if (recordEmpty[i])
                    {
                        // 如果角度为空，默认设为0度
                        layer1.m_theta = 0.0;
                    }
                    else
                    {
                        // 解析当前层的角度
                        layer1.m_theta = convertToReal (dividedEntryInfo[recordIndex[i]]);
                    }
                    break;

                case PCOMPLayIndex_SOUT:
                    // 修剪空格并检查是否为剪切输出标志
                    boost::trim (dividedEntryInfo[recordIndex[i]]);
                    if ("YES" == dividedEntryInfo[recordIndex[i]])
                    {
                        layer1.m_sout = 1.0;  // 表示剪切输出为YES
                    }
                    break;

                default:
                    break;
                }
            }

            // 将解析后的层数据添加到tempData的m_secData中。
            tempData.m_secData.push_back (layer1);
        }
    }
    /**
     * @brief 解析分割的输入信息，并将解析结果存储到PCOMPData数据结构中。
     *
     * 此函数处理分割输入信息的多行数据（通常是有限元分析中的PCOMP卡片），
     * 遍历每一行和每一层，并将每一层的数据提取并存储到`tempData`结构中。
     *
     * @param dividedEntryInfo 包含分割的输入信息的字符串向量。
     * @param tempData 用于存储解析后层数据的PCOMPData数据结构。
     */
    void parseLayData (std::vector<std::string>& dividedEntryInfo, SG::DataStructure::FEM::PCOMPData& tempData)
    {
        // 计算输入信息的行数，每行包含10个元素。
        size_t const rowSize = dividedEntryInfo.size () / 10;

        // 从第二行开始循环遍历每一行的数据。
        for (size_t curRowIndex = 1; curRowIndex < rowSize; curRowIndex++)
        {
            // 每行有两层数据，因此需要对每一层进行处理。
            for (size_t curply = 0; curply < 2; curply++)
            {
                // 计算每一层的基础索引，基本索引通过行索引和层索引计算得到。
                size_t const basicIndex = curRowIndex * 10 + curply * 4 + 1;

                // 调用函数解析每一层的数据，并将结果存储到tempData结构中。
                parseSingleLayData (dividedEntryInfo, basicIndex, tempData);
            }
        }
    }

}  // namespace
SG::DataStructure::Common::Status BDF::BULKDATA::ParsePCOMP (const std::shared_ptr<SG::DBManager::DBServiceFactory>& dbServiceFactory,
                                                             std::vector<std::string>&                               dividedEntryInfo)
{
    SG::DataStructure::FEM::PCOMPData tempData;

    // TODO(bug 001) m_dominId的正确填写
    tempData.m_domainId = 1;

    parseGeneralData (dividedEntryInfo, tempData);

    parseLayData (dividedEntryInfo, tempData);

    // 填充Z默认值

    bool const isEmpty =
        std::all_of (dividedEntryInfo[PCOMPIndex_Z0].begin (), dividedEntryInfo[PCOMPIndex_Z0].end (), [] (char c) { return c == ' '; });
    if (isEmpty)  // z0是默认值
    {
        tempData.m_offset = 0;
        for (size_t i = 0; i < tempData.m_secData.size (); i++)
        {
            tempData.m_offset += tempData.m_secData[i].m_thickness;
        }

        tempData.m_offset *= -0.5;
        
        // 如果是对称层，则需要将偏移量乘以2
        if(tempData.m_lam == SG::DataStructure::FEM::LaminateOption_SYM)
        {
            tempData.m_offset *= 2;
        }
    }

    // 填充nplies
    // TODO(bug 001) 需要确定对称时铺层数量是否需要跟更改
    tempData.m_nplies = static_cast<SG::DataStructure::Common::I64> (tempData.m_secData.size ());

    return dbServiceFactory->get<IPCOMPService> ()->Add (tempData);
}
